import { toSecond } from "../tool";
import { Card } from "./Card";
import { Context } from "./Context";
import { Effect, EffectSubClass, EffectType } from "./Effect";
import { BeingControlledError, CardNotFindError, MethodNotFindError, RoleCardMaxError } from "./errors";
import { Game } from "./Game";
import { Hurt } from "./Hurt";
import { FriendMessage, Message } from "./Message";
import { Method, MethodSubClass } from "./Method";
import { Shield } from "./Shield";


export enum RoleStatus { "Active", "Die" }

/**
 * 注意实现 静态属性 roleName、roleDescript、methods（原生技能） 
 */
export abstract class Role {
    /** 角色手中牌数上限 */
    static roleCardMax = 20;
    /** 角色介绍 */
    static roleDescription: string = "忘记写介绍了";

    /** 角色的名字 */
    static roleName: string = "忘记写名字了";
    /** 使用者的名字 */
    public userName: string;
    /** 使用者的ID */
    public userId: string;
    // 角色属于的某个游戏
    public game: Game;

    public message: Message;

    constructor(userId: string, userName: string, game: Game) {
        this.userId = userId;
        this.userName = userName;
        this.game = game;
        this.message = new FriendMessage(this.game.client, userId, userName, game.groupId);

        // 将原生的技能进行添加
        (this.constructor as typeof Role).methods.map(methodClass => {
            this.addMethod(methodClass)
        })
    }

    /** 提供额外的数据存储功能 */
    private data: { [key: string]: any } = {}


    /** 最大生命值 */
    protected abstract MAXHP: number;
    /** 角色的生命值 */
    protected abstract HP: number;
    /** 护盾 */
    protected shield: Shield = new Shield(0); //默认护盾值 = 0
    /** 物理防御 */
    protected abstract PD: number;
    /** 法术防御 */
    protected abstract MD: number;
    /** 物理攻击 */
    protected abstract PA: number;
    /** 法术攻击 */
    protected abstract MA: number
    /** 角色的技能(原生的) */
    static methods: typeof MethodSubClass[] = [];
    /** 角色的技能(动态的) */
    public methods: Method[] = [];
    /** 角色的状态 */
    private status: RoleStatus = RoleStatus.Active
    /** 角色手中的纸牌 */
    public cards: Card[] = [];
    /** 角色身长的效果（临时属性） */
    protected effects: Effect[] = []
    /** 角色的所有增益效果 */
    get buffs() {
        return this.effects.filter(e => e.type == EffectType.buff)
    }
    /** 角色的减益效果 */
    get debuffs() {
        return this.effects.filter(e => e.type == EffectType.debuff)
    }

    /** 增加角色的控制时间(被控制时无法接受指令) */
    protected control = 0;
    public addControlTime(time: number) {
        if (this.control < Date.now()) {
            this.control = Date.now();
        }

        this.control += time;
    }

    /** 返回剩余被控制的时间，最小值是 0  */
    public getContrlTime() {
        const time = this.control - Date.now()
        return time < 0 ? 0 : time;
    }

    /** 判断角色现在是否被控制 */
    public hasControl() {
        return this.getContrlTime() > 0;
    }


    /** 额外的属性操作 */
    getAttrbuite(key: string, defaultValue?: any) {
        return this.data[key] ?? defaultValue;
    }
    /** 额外的属性操作 */
    setAttrbuite(key: string, value: unknown) {
        this.data[key] = value;
    }

    /** 返回最大生命值 */
    getMAXHP() {
        return this.MAXHP;
    }
    /** 设置最大生命值 */
    setMaxHP(MaxHP: number) {
        this.MAXHP += MaxHP;
    }

    /** 
     * 设置血量。(扣除血量不要直接调用这个方法，调用onHurt方法使其扣血。)
     * 不是直接改变，而是在原来的基础上做改变。
     */
    @broadcast(({ oldProp, newProp }) => `生命值 ${newProp}(${parseInt(newProp) - parseInt(oldProp)})`)
    setHP(n: number) {
        n = Math.floor(n); // 将 n 取整
        this.HP += n;
        if (this.HP > this.MAXHP) {
            this.HP = this.MAXHP;
        }
        if (this.HP <= 0) {
            this.status = RoleStatus.Die;
            this.message.add("你以阵亡。")
            this.effects.forEach(e => e.clear());// 移除所有效果
            this.HP = 0;//最低不能小于0;
            this.game.onDirRole(this);//通知游戏 有角色阵亡
        }
    }
    /** 获取当前生命值 */
    getHP() {
        return this.HP;
    }
    /** 获取当前以损失生命值 */
    getLossHP() {
        return this.getMAXHP() - this.getHP();
    }

    /** 获取自己的状态 返回 RoleStatus */
    getStatus() {
        return this.status
    }

    @broadcast(({ oldProp, newProp }) => `角色状态变化 ${oldProp}->${newProp}`)
    setStatus(status: RoleStatus) {
        this.status = status;
    }

    /** 给角色添加一张牌 */
    @beingControlled()
    addCard(card: Card) {
        if (this.cards.length > Role.roleCardMax) {
            throw new RoleCardMaxError()
        }

        if (this.hasControl()) {
            throw new BeingControlledError();
        }

        this.cards.push(card);
        this.message.add(`获取一张手牌[${card.getName()}]`);
    }

    /** 依据名字出牌 */
    @beingControlled()
    outCard(cardName: string, context: Context) {
        const index = this.cards.findIndex(e => e.getName() == cardName);
        const card = this.cards[index];
        if (card === undefined) {
            throw new CardNotFindError()
        } else {
            this.message.add(`打出手牌[${card.getName()}]`);
            card.outHook(context); // 如果正常调用，则继续下一步
            this.cards.splice(index, 1); // 将这张牌删除掉
            return card;
        }
    }

    /** 返回角色名称(自己的) */
    getRoleName() {
        return (this.constructor as typeof Role).roleName
    }
    getRoleDescription() {
        return (this.constructor as typeof Role).roleDescription
    }

    /**
     * 判断是否有这个技能
     * @param methodName 
     * @returns 
     */
    hasMethod(methodName: string) {
        const method = this.methods.find(e => e.getName() == methodName);
        return method !== undefined;
    }

    /** 动态的添加一个技能 */
    addMethod(methodClass: typeof MethodSubClass) {
        this.methods.push(new methodClass(this))
        this.message.add(`获得技能 [${methodClass.methodName}]`)
    }

    /** 获取所有的技能名称 */
    getAllMethodInfo() {
        return this.methods.map(e => e.getName() + "(" + toSecond(e.getNextCallTime()) + "S)");
    }

    /** 调用技能 */
    @beingControlled()
    callMethod(methodName: string, context: Context) {
        const method = this.methods.find(e => e.getName() === methodName);
        if (method === undefined) {
            throw new MethodNotFindError();
        } else {
            this.message.add(`释放技能 [${method.getName()}]`)
            method.limieInvoke(context);
        }
    }


    setShield(shield: Shield) {
        this.shield = shield;
        this.message.add(`护盾刷新(${this.shield.get()})`)
    }
    /** 返回护盾对象 */
    getShield() {
        return this.shield;
    }

    /** 
     * 被别人攻击,返回最终伤害
     */
    onHurt(role: Role, hurt: Hurt) {

        // 通过物理和法术防御，计算出免伤率（miss:免疫）
        const PM = this.PD / (this.PD + 1200);
        const MM = this.MD / (this.MD + 1200);

        // 计算实际伤害（reality:实际）
        const PR = Math.floor(hurt.PD * PM); // 受到的物理伤害 * 自身物理免疫率 =  实际物理伤害
        const MR = Math.floor(hurt.MD * MM); // 受到的法术伤害 * 自身法术免疫率 =  实际法术伤害
        const TD = hurt.TD;//真实伤害

        // 总伤害(不包括真实伤害)
        let sum = PR + MR;

        // 计算抵挡量
        const shield = this.shield.test(sum);

        sum = this.shield.use(sum);// 计算抵消后的伤害

        sum += TD;// 累加真实伤害


        this.message.add(`被 [${role.userName}] 攻击`);
        // 实施伤害
        this.setHP(-(sum));

        return sum;
    }
    /** 
     * 对 role 发起普通攻击，返回对 role 造成的伤害。
     * @returns {number} 
     */
    attack(role: Role): number {
        const PA = this.getPhysicsAttack()
        const MA = this.getMagicAttack();
        const hurt = new Hurt(PA, MA, 0);
        this.message.add(`攻击 ${role.userName}`);
        return role.onHurt(this, hurt); // 触发被伤害事件
    }

    /** 通过 class 的方式添加 effect,总体来说，相当于 addEffect 的简写 */
    addEffectByClass(effEctClass: typeof EffectSubClass) {
        const effect = new effEctClass(this);
        this.addEffect(effect);
    }
    /** 增加一个增益属性 */
    addEffect(effect: Effect) {
        this.effects.push(effect)
        effect.active(); // 激活这个effect,让其开始运作
    }

    /** 移除一个增益属性 */
    removeEffect(effect: Effect) {
        const index = this.buffs.findIndex(e => e === effect);
        this.effects.splice(index, 1);
    }

    /**
     * 获取角色的物理攻击属性
     * @returns 
     */
    getPhysicsAttack() {
        return this.PA;
    }
    /**
     * 设置物理攻击
     * @param PA 
     */
    @broadcast(({ oldProp, newProp }) => `物理攻击 ${oldProp}->${newProp}`)
    setPhysicsAttack(PA: number) {
        this.PA += PA;
    }
    /**
     * 获取角色的物理防御属性
     * @returns 
     */
    getPhySicsDefend() {
        return this.PD;
    }
    /**
     * 设置物理防御
     * @param PD 
     */
    @broadcast(({ oldProp, newProp }) => `物理防御 ${oldProp}->${newProp}`)
    setPhySicsDefend(PD: number) {
        this.PD += PD;
    }
    /**
     * 获取角色的法术攻击属性
     * @returns 
     */
    getMagicAttack() {
        return this.MA;
    }
    /**
    * 设置法术攻击
    * @param MA 
    */
    @broadcast(({ oldProp, newProp }) => `法术攻击 ${oldProp}->${newProp}`)
    setMagicAttack(MA: number) {
        this.MA += MA;
    }
    /**
     * 获取角色的法术防御属性
     * @returns 
     */
    getMaginDefend() {
        return this.MD;
    }

    /**
    * 设置法术防御
    * @param MD 
    */
    @broadcast(({ oldProp, newProp }) => `法术防御 ${oldProp}->${newProp}`)
    setMaginDefend(MD: number) {
        this.MD += MD;
    }


    /** 当游戏结束的时候，会调用这个钩子函数 */
    gameOverHook() {
        this.message.close();
    }


}

/**
 * 仅表示 Role 的子类，实际不会运行。
 */
// @ts-ignore
export class RoleSubClass extends Role { }

/**
 * 当属性发生改变时，发出消息
 * prop：改变的属性名字
 * @param frommat 
 * @returns 
 */
function broadcast(frommat: (params: { prop: string, oldProp: string, newProp: string, args: any[] }) => string): MethodDecorator {
    return (target: Object, key: string | symbol, descriptor: PropertyDescriptor) => {
        /** get 方法 */
        const prop = key.toString().substring(3);
        //@ts-ignore
        const getFn = target["get" + prop] as Function;

        const oldFn = descriptor.value as Function;
        descriptor.value = function (this: Role, ...args: any[]) {
            // 获取这个属性之前的值
            const oldProp = getFn.call(this)
            // 改变 ... 
            oldFn.apply(this, args)
            // 获取改变后的值
            const newProp = getFn.call(this);
            // 调用格式化，得到最终字符串
            const message = frommat({ prop, oldProp, newProp, args })
            // 通知这位朋友
            this.message.add(message)
        }
    }
}


/**
 * 判断角色是否被控制，如果被控制则抛出被控制异常
 */
function beingControlled(): MethodDecorator {
    return function (target: Object, key: string | symbol, descriptor: PropertyDescriptor) {
        const oldValue = descriptor.value;
        descriptor.value = function (this: Role, ...args: any[]) {
            if (this.hasControl()) {
                throw new BeingControlledError();
            }
            return oldValue(...args);
        }
    }
}